#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <fcntl.h>
#include <time.h>
#include <sched.h> 
#include <semaphore.h>
#include <signal.h> 

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>

//#include <linux/inet.h>
#include <linux/fs.h> 

#include <asm/ioctl.h>
//#include <asm/poll.h>
#include <poll.h>

#include <pthread.h>

#include "ioctl_drv_usr.h"

typedef struct fpga_pcie {
	//fpga pcie driver device node
	int g_fpga_fd;
	
	//pfpga_usr 64个4M空间的起始地址 
	unsigned char * pfpga_usr[BLK_CNT_4M];
	
	//pfpga_usr_flag地址映射标志
	int pfpga_usr_flag[BLK_CNT_4M];
	
	//fpga recv irq flag 0表示中断未触发，1表示中断触发
	unsigned int irq_msi_coming;
	
	//fpga channel and offset address
	T_DDR_DATA g_ddr_data;
	
	//unsigned int g_irq_time_app;	
} FPGA_PCIE_INFO;

FPGA_PCIE_INFO pcie_dma;

void fpga_mmap_all_usr(int fd)
{
	unsigned long byte_offset = 0;
	unsigned int byte_len = SIZE_4M;
	int i = 0;
	
	for (i=0; i<BLK_CNT_4M; i++) {
		byte_offset = i * byte_len;
		
		if (1 == pcie_dma.pfpga_usr_flag[i]) {
			printf("pfpga_usr[%d] mmap() already, not map again", i);
			continue;
		} else {
			pcie_dma.pfpga_usr[i] = (unsigned char*)mmap(0, byte_len, \
											     PROT_READ|PROT_WRITE, \
											     MAP_SHARED|MAP_LOCKED, \
											     fd, byte_offset);
			if (NULL == pcie_dma.pfpga_usr[i]) {
				perror("mmap() fail... \n\r");
				return;
			} else {
				pcie_dma.pfpga_usr_flag[i] = 1;
				printf("mmap() ok:pfpga_usr[%d]:<%p> \n\r", i, pcie_dma.pfpga_usr[i]);
			}
		}
	}
}

void init_fpga_pcie_dma()
{
	int fd = open ("/dev/fpga_pcie", O_RDWR);	
	if (fd == -1) {
     	printf ("Couldn't open the fpga driver device. \n");
      	exit(EXIT_FAILURE);
    } else {
		pcie_dma.g_fpga_fd = fd;
		fpga_mmap_all_usr(fd);
	}
}

void *fpga_irq_msi_pthd_recv(void *arg) 
{
	int state = 0, ret = 0;
    struct pollfd g_poll_fds; 
	
    memset(&g_poll_fds, 0, sizeof(struct pollfd));
    g_poll_fds.fd = pcie_dma.g_fpga_fd;
    g_poll_fds.events = POLLIN;

    //signal(SIGALRM, sigalrm_fn);     

	while (1)
	{
		ret = poll(&g_poll_fds, 1, -1);		
		
        switch (ret) {												 
		case 0:														 
			printf("poll timeout \n");										 
			break;	
			
		case -1:														 
			printf("poll error \n");									 
			break;
	
		default:
			if (g_poll_fds.revents & POLLIN) {
				//printf("pcie_dma.irq_msi_coming ... \n");
				// alarm(1);  // every 1s 	
				
				// fpga irq_ack flag
				pcie_dma.irq_msi_coming = 1;
			}
			g_poll_fds.revents = 0;
			break;
        }
	}
	
	exit(0);	
}

unsigned long offset_2_virt_all(unsigned int offset)
{
	unsigned int idx = offset / SIZE_4M;  // Integer number
	unsigned int rem = offset % SIZE_4M;  // remainder; complement of a number;
	unsigned long ret = 0;
	
	ret = (unsigned long)(pcie_dma.pfpga_usr[idx] + rem);
}

void do_irq_channel_recv_data(int idx, int addr_offset, int data_size, unsigned char *buffer)
{
	int cmd = 0;
	unsigned long l_addr = 0;
	unsigned char * ddr_virt = NULL;
	
	l_addr = idx * SIZE_4M + addr_offset * data_size;
	ddr_virt = (unsigned char *)(offset_2_virt_all(l_addr)); 
	//read ddr_virt data
	cmd = IO_GET_DDR_PHY_ADDR;
	pcie_dma.g_ddr_data.addr = l_addr;
	ioctl(pcie_dma.g_fpga_fd, cmd, &(pcie_dma.g_ddr_data));
	memcpy(buffer, ddr_virt, sizeof(data_size));
	
#if 0
	int cnt = 128
	printf("---irq_read:chn-%d<frm-%d>(%d->0x%08X)<phy:0x%lX>---\n", chn, frm_idx, idx,l_addr, g_ddr_data.addr);
	print_dbg2(ddr_virt, 0, cnt);
	if (idx = idx_chn5_TV_SW || idx = idx_chn6_TV_W || idx = idx_chn7_TV_N){
		int i = 1;
		for (i = 1; i < (data_size/SIZE_4M); i++) {
			l_addr += i * SIZE_4M;				
			ddr_virt = (unsigned char*)(offset_2_virt_all(l_addr));
			print_dbg2(ddr_virt, (SIZE_4M - cnt), cnt);
			printf("\n");
		}	
	} else {
		print_dbg2(ddr_virt, (data_size - cnt), cnt);
		printf("\n");
	}
#endif	
}

int fpga_pcie_dma_recv(int channel, int addr_offset, int data_size, unsigned char *buffer)
{
	int cmd = 0;
	int chnnel_x = 0;
	int idx = 0;
	
	if (1 == pcie_dma.irq_msi_coming) {
		pcie_dma.irq_msi_coming = 0;
		
		cmd = IO_GET_IRQ_CHN_NO;
		if (ioctl(pcie_dma.g_fpga_fd, cmd, &(pcie_dma.g_ddr_data)) < 0) {
			printf("ioctl_cmd: IO_GET_IRQ_CHN_NO fail! \n");
			return -1;
		} else {
			chnnel_x = pcie_dma.g_ddr_data.addr;
			if (chnnel_x == channel) {
				switch (channel) {
				case 1:
					idx = idx_chn1_GIR_WN;
					break;
				case 2:
					idx = idx_chn2_TGIR_SW;
					break;
				case 3:
					idx = idx_chn3_NVIR;
					break;
				case 4:
					idx = idx_chn4_SWIR;
					break;
				case 5:
					idx = idx_chn5_TV_SW;
					break;
				case 6:
					idx = idx_chn6_TV_W;
					break;
				case 7:
					idx = idx_chn7_TV_N;
					break;	
				}
				do_irq_channel_recv_data(idx, addr_offset, data_size, buffer);
			} else {
				printf("fail recv:this channel no irq data\n");
				return -1;
			}
		}
	} else {
		printf("no irq msi trigger\n");
		return -1;
	}
}

int fpga_pcie_dma_send(int channel, int addr_offset, int data_size, unsigned char *buffer)
{
	int i = 0;
	int idx = 0;
	unsigned long l_addr = 0;
	unsigned char * ddr_virt = NULL;
	int cmd;
	int arg = 0;
	
	switch (channel) {
	//=======================================================================
	//tx-1: ft --> fpga: (2 * 4M) 
	case 1:
		if (0 != addr_offset || 1 != addr_offset) {
			printf("The current channel is 8M,out-of-offset memory\n");
			return -1;
		}
		if (NULL == buffer) {
			printf("buffer is invalid memory\n");
			return -1;
		}
		idx = idx_tx1_SDI;
		for (i=0; i<addr_offset; i++) {
			l_addr = (idx + i) * SIZE_4M; //40*4096
			ddr_virt = (unsigned char*)(offset_2_virt_all(l_addr));
			memset(ddr_virt, 0x00, sizeof(data_size));
			memcpy(ddr_virt, buffer+(i * SIZE_4M), sizeof(data_size));
			
			//标准协议格式头
			/*ddr_virt[0] = 0x5d; ddr_virt[1] = 0x5d; ddr_virt[2] = 0x5d; ddr_virt[3] = 0x5d;
			ddr_virt[4] = 0x5d; ddr_virt[5] = 0x00; ddr_virt[6] = 0x00; ddr_virt[7] = 0x00;
			ddr_virt[8] = 0x00; ddr_virt[9] = 0x00; ddr_virt[10] = 0x00; ddr_virt[11] = 0x00;
			ddr_virt[12] = 0x00; ddr_virt[13] = 0x00; ddr_virt[14] = 0x00; ddr_virt[15] = 0x00;*/
			
			/*l_addr = (idx+1) * SIZE_4M;
			ddr_virt = (unsigned char*)(offset_2_virt_all(l_addr));
			memset(ddr_virt, 0x00, sizeof(data_size));
			memcpy(ddr_virt, buffer+sizeof(data_size), data_size);*/
		}

		cmd = IO_SET_TX_FPGA_CH1;
		if (ioctl(pcie_dma.g_fpga_fd, cmd, (unsigned long)(&arg)) < 0) {
			printf("ioctl_cmd: IO_SET_TX_FPGA_CH1 fail! \n");
			return -1;
		} else {
			printf("select(%d)-ioctl_cmd: IO_SET_TX_FPGA_CH1(0x%lX) ok! \n", channel, pcie_dma.g_ddr_data.addr);
		}
		break;

	//=======================================================================
	//tx-2: ft --> fpga: (2 * 4M)
	case 2:
		if (0 != addr_offset || 1 != addr_offset) {
			printf("The current channel is 8M,out-of-offset memory\n");
			return -1;
		}
		if (NULL == buffer) {
			printf("buffer is invalid memory\n");
			return -1;
		}
		idx = idx_tx2_VIDEO;
		for (i=0; i<addr_offset; i++) {
			l_addr = (idx + i) * SIZE_4M; //40*4096
			ddr_virt = (unsigned char*)(offset_2_virt_all(l_addr));
			memset(ddr_virt, 0x00, sizeof(data_size));
			memcpy(ddr_virt, buffer+(i * SIZE_4M), sizeof(data_size));
			
			//标准协议格式头
			/*ddr_virt[0] = 0xaa; ddr_virt[1] = 0x55; ddr_virt[2] = 0x11; ddr_virt[3] = 0x38;*/
			
			/*l_addr = (idx+1) * SIZE_4M;
			ddr_virt = (unsigned char*)(offset_2_virt_all(l_addr));
			memset(ddr_virt, 0x00, sizeof(data_size));
			memcpy(ddr_virt, buffer+sizeof(data_size), data_size);*/
		}

		cmd = IO_SET_TX_FPGA_CH2;
		if (ioctl(pcie_dma.g_fpga_fd, cmd, (unsigned long)(&arg)) < 0) {
			printf("ioctl_cmd: IO_SET_TX_FPGA_CH2 fail! \n");
			return -1;
		} else {
			printf("select(%d)-ioctl_cmd: IO_SET_TX_FPGA_CH2(0x%lX) ok! \n", channel, pcie_dma.g_ddr_data.addr);
		}
		break;
		
	//=======================================================================
	//tx-3: ft --> fpga: (4 * 4M) 			
	case 3:
		if (0 != addr_offset || 1 != addr_offset || 2 != addr_offset || 3 != addr_offset) {
			printf("The current channel is 16M,out-of-offset memory\n");
			return -1;
		}
		if (NULL == buffer) {
			printf("buffer is invalid memory\n");
			return -1;
		}
		idx = idx_tx3_PIC;
		for (i=0; i<addr_offset; i++) {
			l_addr = (idx + i) * SIZE_4M; //40*4096
			ddr_virt = (unsigned char*)(offset_2_virt_all(l_addr));
			memset(ddr_virt, 0x00, sizeof(data_size));
			memcpy(ddr_virt, buffer+(i * SIZE_4M), sizeof(data_size));
		}

		cmd = IO_SET_TX_FPGA_CH3;
		if (ioctl(pcie_dma.g_fpga_fd, cmd, (unsigned long)(&arg)) < 0) {
			printf("ioctl_cmd: IO_SET_TX_FPGA_CH3 fail! \n");
			return -1;
		} else {
			printf("select(%d)-ioctl_cmd: IO_SET_TX_FPGA_CH3(0x%lX) ok! \n", channel, pcie_dma.g_ddr_data.addr);
		}
		break;
		
	//=======================================================================
	//tx-4: ft --> fpga: (1 * 4M) 
	case 4:
		if (0 != addr_offset) {
			printf("The current channel is 4M,out-of-offset memory\n");
			return -1;
		}
		if (NULL == buffer) {
			printf("buffer is invalid memory\n");
			return -1;
		}
		idx = idx_tx4_PAL;
		for (i=0; i<addr_offset; i++) {
			l_addr = (idx) * SIZE_4M; //40*4096
			ddr_virt = (unsigned char*)(offset_2_virt_all(l_addr));
			memset(ddr_virt, 0x00, sizeof(SIZE_2M));
			memcpy(ddr_virt, buffer+(i * SIZE_2M), sizeof(SIZE_2M));
		}
		 			
		cmd = IO_SET_TX_FPGA_CH4;
		if (ioctl(pcie_dma.g_fpga_fd, cmd, (unsigned long)(&arg)) < 0) {
			printf("ioctl_cmd: IO_SET_TX_FPGA_CH4 fail! \n");
			return -1;
		} else {
			printf("select(%d)-ioctl_cmd: IO_SET_TX_FPGA_CH4(0x%lX) ok! \n", channel, pcie_dma.g_ddr_data.addr);
		}
		break;
	default:
		printf("channel number is invalid\n");
		return -1;
		break;
	}
	
	return 0;
}

void release_fpga_pcie_dma()
{
	close(pcie_dma.g_fpga_fd);
}

#if 0
int main(int argc, char **argv)
{
#if 1
	int ret = 0;
	pthread_t recv_fpga_pthd;
	ret = pthread_create(&recv_fpga_pthd, NULL, &fpga_irq_msi_pthd_recv, NULL);
	if (0 != ret) {
		printf("recv_fpga_pthd fail=%d! \n\n", ret);
		return ret;
	} else {
		printf("recv_fpga_pthd create ok=%d! \n\n", ret);
	}
	//pthread_join(recv_fpga_pthd, NULL);
	pthread_detach(recv_fpga_pthd);
#endif
	
	//SIZE_4M buffer
	unsigned char buffer[4194304] = {0};
	while(1){
		//fpga app recv working code
		//int fpga_pcie_dma_recv(int channel, int addr_offset, int data_size)
		//channel:1,2,3,4,5,6,7		
		//addr_offset:0,1,2,3	每一个偏移量就是当前通道的SIZE大小	
		/*data_size:
			channel 1-->SIZE_4M
			channel 2,3,4-->SIZE_1M
			channel 5,6-->SIZE_8M
			channel 7-->SIZE_16M
		*/
		ret = fpga_pcie_dma_recv(1, 0, SIZE_4M, buffer);
		if (0 != ret) {
			continue;
		} else {
			//do other work
			printf("buffer extract success\n");
			
			//fpga app send working code
			//int fpga_pcie_dma_send(int channel, int addr_offset, int data_size, unsigned char *buffer)
			//channel:1,2,3,4
			//addr_offset:0,1,2,3	固定是SIZE_4M,每一个偏移量是SIZE_4M的大小,0是起始的第一个4M地址
			/*data_size:
				channel 1-->2*SIZE_4M
				channel 2-->2*SIZE_4M
				channel 3-->4*SIZE_4M
				channel 4-->1*SIZE_4M	(其中将4M分成2*SIZE_2M使用)
			*/
			fpga_pcie_dma_send(1, 0, SIZE_4M, buffer);
			
		}
	}

}
#endif

/*
compile cmd:
============
gcc test_ctrl.c -o test_ctrl

or 
// pthread compile cmd:

gcc test_ctrl.c -lpthread -o test_ctrl

*/